package com.terry.proxy.util;

import cn.hutool.core.collection.CollectionUtil;
import com.terry.proxy.common.MsgDecoder;
import com.terry.proxy.common.MsgEncoder;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.util.AttributeKey;
import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.function.Consumer;

/**
 * 通道连接属性
 *
 * @author allen
 * @date 2022/12/09
 */
@Slf4j
public class SocketCreateUtil {

    public EventLoopGroup bossGroup = new NioEventLoopGroup();
    public EventLoopGroup workerGroup = new NioEventLoopGroup();
    public ChannelFuture channelFuture = null;
    public int port;

    /**
     * 开启服务端异步
     *
     * @param port             端口
     * @param consumer    连接后再执行的处理器
     */
    public void openServerProxy(Integer port, Consumer<ChannelPipeline> consumer) {
        log.info("开启openServerProxy");
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
            .childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) throws Exception {
                    ChannelPipeline pipeline = ch.pipeline();
                    pipeline.addLast(new MsgDecoder(Integer.MAX_VALUE, 0, 4, -4, 0));
                    pipeline.addLast(new MsgEncoder());
                    pipeline.addLast(new IdleStateHandler(40, 8, 0));
                    // pipeline.addLast(channelHandler);
                    consumer.accept(pipeline);
                }

            });
            this.port = port;
            channelFuture = b.bind(port).sync().channel().closeFuture().addListener((ChannelFutureListener) future -> {
                shutdown();
            });
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            closeServerProxy();
        }
    }

    public void closeServerProxy () {
        // 服务器已关闭
        log.info("服务关闭！");
        if (channelFuture != null) {
            channelFuture.channel().close().syncUninterruptibly();
            channelFuture = null;
        }
        if (bossGroup != null) {
            bossGroup.shutdownGracefully();
        }
        if (workerGroup != null) {
            workerGroup.shutdownGracefully();
        }
    }

    public void openServerReal(Integer port, Consumer<ChannelPipeline> consumer) {
        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
        .childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch) throws Exception {
            ChannelPipeline pipeline = ch.pipeline();
            consumer.accept(pipeline);
            // pipeline.addLast(ArrayUtil.toArray(childHandlers, ChannelHandler.class));
            }
        });
        try {
            b.bind(port).get();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void openClientReal(String serverIp, Integer serverPort, Consumer<Bootstrap> consumer) {
        Bootstrap bootstrap = new Bootstrap().group(bossGroup)
                .channel(NioSocketChannel.class);
        consumer.accept(bootstrap);
        bootstrap.connect(serverIp, serverPort).channel().closeFuture().addListener((ChannelFutureListener) future -> {
            shutdown();
        });
    }

    public void shutdown(){
        log.info("关闭bossGroup workerGroup");
        if (bossGroup != null) {
            bossGroup.shutdownGracefully();
        }
        if (workerGroup != null) {
            workerGroup.shutdownGracefully();
        }
    }

    public void openClientProxy(String serverIp, Integer serverPort, Consumer<ChannelPipeline> consumer) {
        Bootstrap bootstrap = new Bootstrap().group(bossGroup)
                .channel(NioSocketChannel.class);
        bootstrap.handler(
            new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel socketChannel) throws Exception {
                    ChannelPipeline pipeline = socketChannel.pipeline();
                    pipeline.addLast(new MsgDecoder(Integer.MAX_VALUE, 0, 4, -4, 0));
                    pipeline.addLast(new MsgEncoder());
                    pipeline.addLast(new IdleStateHandler(40, 8, 0));
                    consumer.accept(pipeline);
                }
            }
        );
        bootstrap.connect(serverIp, serverPort).channel().closeFuture().addListener((ChannelFutureListener) future -> {
            shutdown();
        });
    }

    // 访客ID
    public static AttributeKey<String> VID = AttributeKey.valueOf("vid");

    // 客户端ID
    public static AttributeKey<String> CID = AttributeKey.valueOf("cid");

    // 隧道ID
    public static AttributeKey<Integer> TID = AttributeKey.valueOf("tid");

    /**
     * 关闭通道
     * @param channelMap
     * @param vid
     */
    public static void closeChannel(Map<String, Channel> channelMap, String vid){
        if (StringUtil.isNullOrEmpty(vid)) {
            return;
        }
        Channel channel = channelMap.get(vid);
        if (null != channel) {
            channel.attr(VID).set(null);
            channelMap.remove(vid);
            channel.close();
        }
    }

    /**
     * 关闭全部通道
     * @param channelMap
     */
    public static void closeChannelAll(Map<?, Channel> channelMap){
        if (CollectionUtil.isEmpty(channelMap)) {
            return;
        }
        for (Channel channel : channelMap.values()) {
            channel.attr(VID).set(null);
            channel.close();
        }
        channelMap.clear();
    }
}
